START:
#define c - s 客户到服务端;
2.2 Web 和 HTTP
2.2.1 HTTP 概况
Web的应用层协议是超文本传输协议(HTTP),其由两个程序实现:
- 客户端程序。
- 服务器程序。
这两者运行在不同的端系统中,通过交换 http 报文进行会话。
某些 Web 术语:
Web 页面:是由对象组成的。一个对象只是一个文件,如一个 HTML 文件,一个 jpeg 图片,且它们都可以通过一个 URL 地址寻址。
多数 Web 页面含有一个 HTML 基本文件(basic HTML file)以及几个引用对象。
URL:每个 URL由两部分组成:
- 存放对象的服务器主机名
- 对象的路径名。
如:
http://www.someSchool.edu/someDepartment/picture.gif其中www.someSchool.edu是主机名,后面的是对象的路径名。
Web 浏览器(browser)实现了 HTTP 客户端,所以在 Web和 HTTP 交替使用“浏览器”和”客户端“。
Web 服务器(server)实现了 HTTP 服务器端。
HTTP 定义了 Web 客户向 Web 服务器请求 Web 页面的方式,以及服务器向客户发送 Web 页面的方式。
其基本思想是客户发送请求,浏览器发送请求报文,服务器收到报文,并用包含请求的文件的报文进行相应。
HTTP 使用 TCP 作为支撑运输协议。
http 首先在 c-s中建立连接,然后通过套接字发送数据。
客户端的套接字的两端为客户进程和 TCP。
服务器端的套接字的两端为服务器进程和 TCP。
一旦客户的报文通过套接字,报文就脱离客户控制,并进入 TCP 的控制。
TCP 为 HTTP 提供可靠数据传输服务,一个客户发送的每个 HTTP 请求报文都能够被服务器完整地接收,服务器同理。
这里可以看到分层体系的最大优点:HTTP 协议不用担心数据丢失,也不用关注 TCP 从网络的数据丢失和乱序故障中恢复的细节。
服务器向客户发送被请求的文件,而不存储任何关于该客户的状态信息,因为 HTTP 服务器并不保存关于客户的任何信息,所以 HTTP 是一个无状态协议。
2.2.2 非持续连接和持续连接
- 每个请求/响应对是经一个单独的 TCP 连接发送的,是非持续连接。
- 所有请求/响应经相同的 TCP 连接发送的,是持续连接。
HTTP 在默认情况下使用持续连接,也能被配置成非持续连接。
1.非持续连接
假设一个 Web 界面有 11 个元素,当用户请求时,会产生 11 个 TCP 连接。
HTTP 规范仅定义了在 S - C 的通信协议。
估算一个 TCP 花费的时间
往返时间(Round-trip Time,RTT)
一个短分组从 c-s,再从 s-c的时间,它包括分组传播时延、分组在中间路由器和交换机的排队时延和分组处理时延。
三次握手
- c-s发送一个小 TCP 报文段最后 c-s 返回确认;
- 服务器用一个小 TCP 报文做出确认和响应;
- 最后 c-s 返回确认。
1,2占用了一个 RTT。在第三部分中,客户结合第三部分向该 TCP 连接发送一个 HTTP 请求报文。一旦请求到达服务器,服务器就在该 TCP 连接上发送 HTML 文件。此部分占用了一个 RTT。
因此,总响应时间就是两个 RTT加上服务器传输 HTML 文件的时间。
缺点
- 必须为每个请求的对象建立和维护一个全新的连接,需要给每个连接分配 TCP 缓冲区和保持 TCP 变量。这给 Web 服务器带来了严重的负担。
- 每个对象经受两倍 RTT 的交付时延。一个 RTT 用于创建连接,一个用来传输文件。
2. 持续连接
服务器在发送响应后保持该 TCP 连接打开,在相同的 CS 中间,后续请求和响应报文能够通过相同的连接进行传送,特别是一个完整的 Web 界面。
一般来说如果一条连接经过一定时间间隔仍未被使用,HTTP 服务器就关闭该连接。
HTTP 服务器默认模式是使用带流水线的持续连接。
2.2.3 HTTP 报文格式
HTTP 的报文有两种:
- 请求报文;
- 响应报文。
1. 请求报文
特点:
报文是用普通的 ACSII 文本所写;
每行由一个回车和换行符结束;
一个请求报文至少为一行,或者更多行。
报文第一行为请求行(request line),后继行为首部行(header line);
请求行:
字段:
- 方法字段;
- URL 字段;
- HTTP 版本字段。
方法字段可以取不同的值,包括:GET, POST, HEAD, PUT, DELETE。
绝大多数的 HTTP 请求报文使用的都是 GET 方法。
当一个浏览器请求一个对象时,使用 GET 方法。
在 URL 字段带有请求对象的标识。
版本字段是自解释的。
我们经常会犯一个错误:当我们更新代码时,却忘记更新相应的注释。不友好的注释并不会影响代码的执行,但使我们的调试和阅读带来极大困扰,注释描述的是一种逻辑,而代码确是另外一种,结果会浪费我们大量时间来搞懂这段代码的意思,更糟糕的是这样的注释很可能误导我们。
这并不是说注释完全没有必要,优秀的代码有具有相应优秀的注释。我们可以利用某些编程技术来减少我们的注释,使我们的代码更加自解释。这不仅仅使我们的代码更加容易理解,还有助于改善项目的整体设计。
这样的代码通常被称为自解释的代码。
首部行Host: 指明了对象所在的主机。即使在该主机已经有一条 TCP 连接了,这还是需要的,这是由于 Web缓存的缘故。
- (待注释为什么web缓存需要)
Connection: close首部行,告诉服务器不要麻烦地用持续连接,而是发送完被请求地对象就关闭这条连接。
User-agent:首部行指明用户代理,发送请求地浏览器的类型。服务器可以利用这个首部行向不同类型的用户代理发送相同对象的不同版本。
Accept-language:首部行标识用户想要得到的版本。(是 HTTP 协商首部之一)。
在首部行后还有一个实体体(entity body)。
使用 get 方法时,实体体为空,而使用 post 方法时才使用。
当用户提交表单时,常常使用 post 方法。
当使用post报文时,用户仍可向服务器请求一个 web 页面,但页面显示的特定内容依赖于用户在表单字段中的输入值。
如果方法字段的值为post的话,实体体包含的就是用户在表单中的输入值。
用表单生成的请求不是必须使用post方法。
HTML 中,常使用 GET 方法并在表单字段所请求的 url中包含所输入的数据。
例如,一个表单使用get方法,它有两个字段,分别为:monkeys、bananas。该 URL 结构为
www.somesite.com/animalsearch? monkeys&bananas。
head 方法类似 get 方法。当服务器收到 head 方法的请求时,只会用一个报文响应,但不返回请求对象,常用于开发者的调试跟踪。
put 方法常和 web 发行工具联合使用,允许用户上传对象到指定 web 服务器的路径上。
delete 方法允许用户或应用程序删除 web 服务器的对象。
2. HTTP 响应报文
响应报文如下:
1 | HTTP/1.1 200 OK |
第一行为初始状态行(status line),2 - 7 行为首部行(header line),然后是实体体(entity body)。
实体体部分为主要部分,包含了请求对象的本身(一堆 data)。
状态行有三个字段:
- 协议版本字段;
- 状态码;
- 相应状态信息。
在这个报文中,状态行指示服务器正在使用 HTTP/1.1 并且一切正常,服务器已经找到并正在发送所请求的对象。
第二行,发完就关。
第三行,服务器找到这个文件,插进报文,发出的时间。
不是指这个文件的创建或者最后修改时间。
第四行,该报文是由 Apache 服务器发出来的,类似 User-Agent: 首部行。
第五行,这个对象创建或者最后修改时间。此首部行对于缓存服务器很有用。
第六行,被发送对象的字节数。
第七行,对象类型。
该对象类型应当由
Connent-Type:首部行指出,而非文件的扩展名。
常用的状态码:
| 状态码 | 作用 |
|---|---|
| 200 OK | 请求成功,信息在返回的响应报文中。 |
| 301 Moved Permanently | 请求的对象已经被永久转移了,新的 URL 定义在响应报文的 Location:首部行中。客户软件将自动获取新的 URL。 |
| 400 Bad Request | 一个通用差错代码,指示该请求不能被服务器理解。 |
| 404 Not Found | 被请求的文档不在服务器上。 |
| 505 HTTP Version Not Support | 服务器不支持请求报文使用的 HTTP 协议版本。 |
浏览器产生的首部行与很多因素有关系,例如:
- 浏览器的类型和协议版本;
- 浏览器的用户配置(如喜爱的语言);
- 浏览器是否有一个缓存,但是可能是超期的对象版本;
在产品、版本、配置上都有差距,所有这些都会影响报文包括的首部行。
2.2.4 用户和服务器的交互: cookie
HTTP 是无状态的,这简化了服务器的设计。
服务器向客户发送被请求的文件,而不存储任何关于该客户的状态信息,因为 HTTP 服务器并不保存关于客户的任何信息,所以 HTTP 是一个无状态协议。
当一个 Web 站点想要识别用户时,HTTP 使用了 cookie,允许站点对于用户进行跟踪。
- 插入图片
cookie 如上图所示,有四个组件:
- 在 HTTP 响应报文中的一个 cookie 首部行;
- 在 HTTP 请求报文中的一个 cookie 首部行;
- 在用户端系统保留着一个 cookie 文件,并由用户的浏览器进行管理;
- 位于 Web 站点的一个后端数据库。
cookie 可以用于标记一个用户:用户首次访问一个站点,可能需要提供一个用户标识,在后续会话中,浏览器向服务器发送一个 cookie 的首部,从而向该服务器标识了用户。因此 cookie 可以在无状态的 HTTP 之上建立一个用户会话层。
2.2.5:Web 缓存
Web 缓存器(Web Cache)又叫代理服务器(proxy server)它能代表初始 Web 服务器满足 HTTP 请求的网络实体。
Web 缓存器有自己的磁盘存储空间,并在存储空间中保留着最近请求过对象的副本。
工作原理:
假设浏览器正在请求对象 http:// www.someschool.edu/campus.gif/ 会发生如下情况:
- 浏览器创建一个到缓存器的 TCP 连接,并向缓存器中的对象发送一个 http 请求。
- 缓存器检查本地是否有该对象的副本。若有,向客户浏览器用响应报文返回该对象。
- 若无,就打开一条与该对象的初始服务器
www.someschool.edu的 TCP 连接,并发送一条请求报文。 - 当缓存器收到对象时,现在本地存储一份副本,并向客户用响应报文发送该副本。
Web 缓存器既是服务器又是客户。
缓存器通常由 ISP 购买并安装。
部署的原因:
- 降低对客户请求的响应时间,特别是客户与初始服务器间的瓶颈带宽大大小于客户与缓存器的瓶颈带宽。
- 减少一个机构接入链路到因特网的通信量,使机构不必急于增加带宽,减少费用。
- 减少因特网上的网络流量。
通过使用 内容分发网络(CDN)缓存器正在互联网上发挥着越来越重要的作用。
2.2.6 条件 GET 方法
存放在缓存器中的对象副本可能是陈旧的。
条件 GET 方法是 HTTP 中的一种机制,用来让缓存器证实它的对象是最新的。
如果:
- 请求报文使用 GET 方法;
- 请求报文包含
If-Modified-Since:首部行。
则该报文为条件 GET 报文。
例子:
一个代理缓存器代表一个请求浏览器,向某服务器发送请求报文:
1 | GET /fruit/kiwi.gif HTTP/1.1 |
之后,该服务器发送具有被请求对象的响应报文:
1 | HTTP/1.1 200 OK |
然后缓存器将对象转发,并在本地缓存。同时存储了最后修改日期。
一周后,又有同样的请求,利用 GET 方法。
1 | GET /fruit/kiwi.gif HTTP/1.1 |
注意If-Modified-Since:首部行的值正好等于一周前报文中Last-Modified:的值。
该条件get 报文的意思为,仅当执行日期之后,对该对象修改,才发送对象。
响应报文为:
1 | HTTP/1.1 304 Not Modified |
对于响应,仍发送了一个响应报文,但并未包含对象,因为纯属浪费带宽,并增加响应时间。
状态行中304 Not Modified告诉缓存器可以使用该对象,能向请求的浏览器转发它缓存的该对象副本。